/*
 * Copyright (c) 2022-2022 All rights Reserved.
 */

package com.driving.core.util;

import java.util.Base64;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;

import com.driving.core.exception.DrivingException;
import com.driving.core.util.Constant.Audience;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;

/**
 * Token生成与解析
 *
 * @author SAGITTAR
 * @since 2017
 */
@Slf4j
public class JsonWebToken {

    private static final String BASE64_ENCODED_SECRET_KEY;
    static {
        BASE64_ENCODED_SECRET_KEY = Base64.getEncoder().encodeToString("PASSWORD".getBytes());
    }

    private JsonWebToken() {
        throw new Error();
    }

    /**
     * 生成该用户的Token信息
     *
     * @param id 用户ID
     * @param audience 角色
     * @param mobile 手机号
     * @param password 密码
     * @param isExpired 是否过期
     * @return 生成该用户的Token信息
     */
    public static String compact(int id, Audience audience, String mobile, String password, boolean... isExpired) {
        Claims claims = Jwts.claims();
        claims.setId(Integer.toString(id));
        claims.setAudience(audience.toString());
        claims.setIssuer(mobile);
        claims.setSubject(password);
        Calendar calendar = Calendar.getInstance();
        int dayOfYear = 7;
        if (Objects.nonNull(isExpired) && isExpired.length > 0 && isExpired[0]) {
            dayOfYear = -1;
        }
        calendar.add(Calendar.DAY_OF_YEAR, dayOfYear);
        claims.setExpiration(calendar.getTime());
        JwtBuilder jwtBuilder = Jwts.builder().signWith(SignatureAlgorithm.HS512, BASE64_ENCODED_SECRET_KEY);
        return jwtBuilder.setClaims(claims).compact();
    }

    /**
     * 解析生成的Token信息
     *
     * @param genCompact 生成该用户的Token信息
     * @return 解析生成的Token信息
     */
    public static Map<String, String> parseStudent(String genCompact) {
        return parse(genCompact, Audience.STUDENT);
    }

    /**
     * 解析生成的Token信息
     *
     * @param genCompact 生成的Token信息
     * @return 解析生成的Token信息
     */
    public static Map<String, String> parseCoach(String genCompact) {
        return parse(genCompact, Audience.COACH);
    }

    /**
     * 解析生成的Token信息
     *
     * @param genCompact 生成的Token信息
     * @return 解析生成的Token信息
     */
    public static Map<String, String> parseAdmin(String genCompact) {
        return parse(genCompact, Audience.ADMIN);
    }

    /**
     * 解析生成的Token信息
     *
     * @param genCompact 生成的Token信息
     * @param audience 角色
     * @return 解析生成的Token信息
     */
    public static Map<String, String> parse(String genCompact, Audience audience) {
        if (StringUtils.isBlank(genCompact)) {
            return Collections.emptyMap();
        }
        Jws<Claims> jwsClaims;
        try {
            jwsClaims = Jwts.parser().setSigningKey(BASE64_ENCODED_SECRET_KEY).parseClaimsJws(genCompact);
        } catch (MalformedJwtException e) {
            log.error(e.getLocalizedMessage(), e);
            throw new DrivingException("用户未登录");
        }
        Claims claims = jwsClaims.getBody();
        Audience claimsAudience = Audience.from(claims.getAudience());
        if (!claims.getExpiration().after(Calendar.getInstance().getTime()) || claimsAudience != audience) {
            return Collections.emptyMap();
        }
        Map<String, String> result = new HashMap<>(6);
        result.put(Constant.ID, claims.getId());
        result.put(Constant.MOBILE, claims.getIssuer());
        result.put(Constant.PASSWORD, claims.getSubject());
        result.put(Constant.AUDIENCE, claims.getAudience());
        return result;
    }

    /**
     * 验证Token是否有效
     *
     * @param genCompact 生成的Token信息
     * @return 验证Token是否有效
     */
    public static Map<String, String> validateToken(String genCompact) {
        Audience audience = Audience.STUDENT;
        Map<String, String> validate = parse(genCompact, audience);
        if (validate.isEmpty()) {
            audience = Audience.COACH;
            validate = parse(genCompact, audience);
            if (validate.isEmpty()) {
                audience = Audience.ADMIN;
                validate = parse(genCompact, audience);
            }
        }
        if (validate.isEmpty()) {
            return Collections.emptyMap();
        }
        int id = Integer.parseInt(validate.get(Constant.ID));
        String password = validate.get(Constant.PASSWORD);
        String newToken = compact(id, audience, validate.get(Constant.MOBILE), password);
        Map<String, String> result = new HashMap<>(4);
        result.put(Constant.ID, String.valueOf(id));
        result.put(Constant.TOKEN, newToken);
        result.put(Constant.AUDIENCE, audience.toString());
        result.put(Constant.PASSWORD, password);
        return result;
    }
}
